#!/usr/bin/evn python

import wsgiref.handlers
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.api import urlfetch


from models import *
from ptzmodels import *
from common.constants import *

import string
import time

import stats, insert, build, errorDetection

webapp.template.register_template_library('common.customtags')

"""
Simple pages
"""
class Home(webapp.RequestHandler):
    def get(self):
        from google.appengine.api import users
        import hashlib
        import re
        
        user = users.get_current_user()
        logInURL = users.create_login_url("")
        logOutURL = users.create_logout_url("")
        
        username = None
        if user:
            username = user.nickname()
        
        #sourceRE = re.compile('(http://[^/]*)')
        
        values = {
            'username' : username,
            'logInURL' : logInURL,
            'logOutURL' : logOutURL
        }
        self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'home.html', values))
        
class About(webapp.RequestHandler):
    def get(self):
        from google.appengine.api import users
        import hashlib
        import re
        
        user = users.get_current_user()
        logInURL = users.create_login_url(self.request.url)
        logOutURL = users.create_logout_url(self.request.url)
        
        username = None
        if user:
            username = user.nickname()
        
        #sourceRE = re.compile('(http://[^/]*)')
        
        values = {
            'username' : username,
            'logInURL' : logInURL,
            'logOutURL' : logOutURL
        }
        self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'about.html', values))

class VFYW(webapp.RequestHandler):
    def get(self):
        from google.appengine.api import users
        import hashlib
        import re
        import urllib2
        import time
        
        user = users.get_current_user()
        logInURL = users.create_login_url(self.request.url)
        logOutURL = users.create_logout_url(self.request.url)
        
        username = None
        if user:
            username = user.nickname()
        
        #sourceRE = re.compile('(http://[^/]*)')

        current_year = time.strftime("%Y", time.gmtime())
        current_month = time.strftime("%m", time.gmtime())
        current_hour = int(time.strftime("%H", time.gmtime())) - 5;
        current_dayinweek = int(time.strftime("%w", time.gmtime()))
        current_dayinmonth = int(time.strftime("%d", time.gmtime()))
        current_saturday = current_dayinmonth - current_dayinweek - 1

        # If saturday and current eastern time (gmt-5) is noon, look for the newer VFYW
        if current_dayinweek == 6 and current_hour >= 12 :
            current_saturday = current_saturday + 7;
            
        archive_url = "http://dish.andrewsullivan.com/" + current_year + "/" + current_month + "/" + str(current_saturday) + "/the-view-from-your-window-contest/"

        response = urllib2.urlopen(archive_url)
        page_to_parse = response.read()
        temp_pos = page_to_parse.find('entry-content')
        page_to_parse = page_to_parse[temp_pos:]
        href_pos = page_to_parse.find('href')
        png_pos = page_to_parse.find('.png')

        #print page_to_parse[href_pos:png_pos+4];
        #page_to_parse = page_to_parse[temp_pos:];
        #page_to_parse = page_to_parse.strip(' \t\n\r');
        #begin_url_pos = page_to_parse.find('href=') + 6;
        #end_url_pos = page_to_parse.find('-popup');
        #image_url = page_to_parse[begin_url_pos:end_url_pos];
        image_url = page_to_parse[href_pos+6:png_pos+4];
        
        values = {
            'username' : username,
            'logInURL' : logInURL,
            'logOutURL' : logOutURL,
            'vfywImageURL' : image_url+"",
            'archiveURL' : archive_url+""
        }
        self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'vfyw.html', values))

class Contribute(webapp.RequestHandler):
    def get(self):
        from google.appengine.api import users
        import hashlib
        import re
        
        user = users.get_current_user()
        logInURL = users.create_login_url(self.request.url)
        logOutURL = users.create_logout_url(self.request.url)
        
        username = None
        if user:
            username = user.nickname()
        
        #sourceRE = re.compile('(http://[^/]*)')
        
        values = {
            'username' : username,
            'logInURL' : logInURL,
            'logOutURL' : logOutURL
        }
        self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'contribute.html', values))
        
class Contact(webapp.RequestHandler):
    def get(self):
        from google.appengine.api import users
        import hashlib
        import re
        
        user = users.get_current_user()
        logInURL = users.create_login_url(self.request.url)
        logOutURL = users.create_logout_url(self.request.url)
        
        username = None
        if user:
            username = user.nickname()
        
        #sourceRE = re.compile('(http://[^/]*)')
        
        values = {
            'username' : username,
            'logInURL' : logInURL,
            'logOutURL' : logOutURL
        }
        self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'contact.html', values))

class SendMail(webapp.RequestHandler):
    def post(self):
        from google.appengine.api import mail
        
        from_name = self.request.get('Name')
        from_email = self.request.get('Email')
        msg = self.request.get('Message')
        anti_spam = int(self.request.get('anti_spam'))

        sender_of_email = 'projectlive3d@gmail.com'
        receiver_of_email = 'admin@projectlive3d.com' #'admin@projectlive3d.com' #'adina.stoica@gmail.com'; #'austin@projectlive3d.com'

        if anti_spam < 10 :
            sbject = '[SPAM] Live3D - message from '+from_name
        else :
            sbject = 'Live3D - message from '+from_name

        body_message = 'From: '+from_name+'\nE-mail: '+from_email+'\nMessage: \n'+msg;
        mail.send_mail(sender_of_email, receiver_of_email, sbject, body = body_message, reply_to = from_email)

        self.redirect("/")

"""
A page to print errors and messages
"""
class Middle(webapp.RequestHandler):
    def get(self):
        from google.appengine.api import users
        import hashlib
        import re
        
        user = users.get_current_user()
        logInURL = users.create_login_url(self.request.url)
        logOutURL = users.create_logout_url(self.request.url)
        
        username = None
        if user:
            username = user.nickname()
        
        #sourceRE = re.compile('(http://[^/]*)')
        
        values = {
            'username' : username,
            'logInURL' : logInURL,
            'logOutURL' : logOutURL
        }
        self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))

"""
Allows users to view a scene.
"""
class ViewScene(webapp.RequestHandler):
    def get(self):
        from google.appengine.api import users
        import hashlib
        import re

        sceneId = self.request.get('scene')
        scene = Scene.get_by_key_name('scene' + str(sceneId))
        
        user = users.get_current_user()
        logInURL = users.create_login_url("/viewScene?scene=" + str(sceneId))
        logOutURL = users.create_logout_url("/viewScene?scene=" + str(sceneId))
        
        votable = False
        username = None
        admin = False
        if user:
            username = user.nickname()
            keyname = hashlib.md5(user.nickname() + '|' + str(sceneId)).hexdigest()
            rateObj = Rating.get_by_key_name(keyname)
            if not rateObj:
                votable = True
            if username in ADMIN_EMAILS:
                admin = True

        if scene:
            if scene.is_private:
                #print user==scene.owner
                if (user != scene.owner) and (admin == False):
                    values = {
                        'message': 'You do not have permission to view this scene. Click <a href="http://www.projectlive3d.com/">here</a> to return to the main page.',
                        'username' : username,
                        'logInURL' : logInURL,
                        'logOutURL' : logOutURL
                    }
                    self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
                    return
            meshes = Mesh.all().filter('scene =', scene).fetch(limit=100)
            calibration = Calibration.get_by_key_name('calib' + str(sceneId));
        else:
            values = {
                'message': 'Scene not found.',
                'username' : username,
                'logInURL' : logInURL,
                'logOutURL' : logOutURL
            }
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
            return
        
        allCorrs = Correspondence.all().filter('scene = ', scene)
        numPts = allCorrs.count()
        
        stars = [False] * 5
        for i in range(5):
            stars[i] = (scene.ratingAverage > i)
        
        credit = ''
        sourceRE = re.compile('(http://[^/]*)')
        match = sourceRE.match(scene.url)
        if match:
            credit = match.group(1)
        
        values = {
            'scene' : scene,
            'meshes' : meshes,
            'calibration' : calibration,
            'username' : username,
            'logInURL' : logInURL,
            'logOutURL' : logOutURL,
            'votable' : votable,
            'stars' : stars,
            'credit' : credit,
            'numPts': numPts,
            'admin': admin
        }
        self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'viewScene.html', values))
        
"""
Just the maps side.
"""
class Embed(webapp.RequestHandler):
    def get(self):
        from google.appengine.api import users
        import hashlib
        import re
        
        sceneId = self.request.get('scene')
        scene = Scene.get_by_key_name('scene' + str(sceneId))
    
        if scene:
            meshes = Mesh.all().filter('scene =', scene).fetch(limit=100)
            calibration = Calibration.get_by_key_name('calib' + str(sceneId));
        else:
            values = {'message': 'Scene not found.'}
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
            return
        
        width = self.request.get('w')
        height = self.request.get('h')
        if not width:
            width = 300
        if not height:
            height = 300
        
        values = {
            'scene' : scene,
            'meshes' : meshes,
            'calibration' : calibration,
            'width' : width,
            'height' : height
        }
        self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'embed.html', values))

"""
Allows users to modify the contents of a scene. (delete parts of this scene)
"""
class ModifyScene(webapp.RequestHandler):
    def get(self):
        from google.appengine.api import users
        from common.commonDB import authorizedToDeleteMesh, authorizedToDeleteCorrespondence
        import common.constants
        
        sceneId = self.request.get('scene')
        scene = Scene.get_by_key_name('scene' + str(sceneId))
        if scene:
            correspondences = Correspondence.all().filter('scene =', scene).fetch(limit=100)
            meshes = Mesh.all().filter('scene =', scene).fetch(limit=100)
            calibration = Calibration.get_by_key_name('calib' + str(sceneId));
        else:
            values = {'message': 'Scene not found.'}
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
            return
        
        logInURL = users.create_login_url("/modifyScene?scene=" + str(sceneId))
        logOutURL = users.create_logout_url("/viewScene?scene=" + str(sceneId))
        
        user = users.get_current_user()
        if not user:
            # make the user log in.
            
            values = {
                'user': user,
                'scene': None,
                'logInURL': logInURL,
                'logOutURL': logOutURL,
                }
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'modifyScene.html', values))
            return          
    
        scene = Scene.get_by_key_name('scene' + str(sceneId))
        
    
        if scene.is_private:
            if user != scene.owner and user.nickname() not in ADMIN_EMAILS:
                values = {
                    'message': 'You do not have permission to view this scene. Click <a href="http://www.projectlive3d.com/">here</a> to return to the main page.',
                    'username' : user.nickname(),
                    'logInURL' : logInURL,
                    'logOutURL' : logOutURL
                }
                self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
                return
        orphanCorrs = []
        for corr in correspondences:
            if corr.parentMeshes == 0:
                corr.deletable = authorizedToDeleteCorrespondence(corr, user)
                orphanCorrs.append(corr)
        
        for mesh in meshes:
            mesh.deletable = authorizedToDeleteMesh(mesh, user)
        
        admin = False
        if user.nickname() in ADMIN_EMAILS:
            admin = True
    
        values = {
                'user': user,
                'username': user.nickname(),
                'scene': scene,
                'pointCorrespondences' : orphanCorrs,
                'allCorrespondences' : correspondences,
                'meshes' : meshes,
                'calibration' : calibration,
                'logInURL': logInURL,
                'logOutURL': logOutURL,
                'admin': admin,
                }
        self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'modifyScene.html', values))

class AdminModify(webapp.RequestHandler):
    def post(self):
        from common.commonDB import deleteCorrespondence, deleteMesh, deleteScene, deleteCalibration
        from google.appengine.api import users
        import common.meshwriter
        import calibrate
    
        sceneId = self.request.get('scene')
        scene = Scene.get_by_key_name('scene' + sceneId)
        if not scene:
            values = {'message': 'Scene ' + scene + ' does not exist.'}
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
            return
        
        user = users.get_current_user()
        if not user:
            username = None
        else:
            username = user.nickname()
        logInURL = users.create_login_url(self.request.url)
        logOutURL = users.create_logout_url(self.request.url)
        
        if (not (scene.is_private and user == scene.owner)) and (not user or user.nickname() not in ADMIN_EMAILS):
            values = {
                    'message': 'Must be an administrator to use adminModify page.',
                    'username' : username,
                    'logInURL' :logInURL,
                    'logOutURL' :logOutURL
                    }
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
            return
            
        rewriteMesh = self.request.get('rewriteMesh')

        deleteTheScene = self.request.get('deleteScene')
        redirTo = self.request.get('redirTo')

        recalibrateScene = self.request.get('recalibrate')
        
        if rewriteMesh:
            mesh = Mesh.get_by_key_name('mesh' + str(rewriteMesh));
            corrs = []
            for i in mesh.correspondenceMapping:
                corrs.append(Correspondence.get_by_key_name('corr' + str(i)))
                
            triangleIndices = mesh.triangles
            width = scene.width
            height = scene.height
                
            originLat, originLon, originAlt, \
            daePositions, daeNormals, daeUVs, daeTriangles, \
            nPositions, nNormals, nUVs, nTriangles = \
                common.meshwriter.writeMeshFile(corrs, triangleIndices, width, height)
            
            mesh.daePositionArray = daePositions
            mesh.daeNormalArray = daeNormals
            mesh.daeUVArray = daeUVs
            mesh.daeTriangles = daeTriangles
            mesh.nPositions = nPositions
            mesh.nNormals = nNormals
            mesh.nUVs = nUVs
            mesh.nTriangles = nTriangles
            mesh.originLatitude = originLat
            mesh.originLongitude = originLon
            mesh.originAltitude = originAlt
            mesh.put()
            self.redirect("/modifyScene?scene=" + sceneId)
        
        elif deleteTheScene:
            allCorrs = Correspondence.all().filter('scene = ', scene)
            allMeshes = Mesh.all().filter('scene = ', scene)
            calib = Calibration.get_by_key_name('calib' + str(scene.index))
            for corr in allCorrs:
                deleteCorrespondence(corr.index)
            for mesh in allMeshes:
                deleteMesh(mesh.index)
            deleteCalibration(scene.index)
            deleteScene(scene.index)

            if len(redirTo) > 0 :
                self.redirect("/"+redirTo)
            else :
                self.redirect("/viewAll")
            
        elif recalibrateScene:
            calibrate.calibrateCamera(scene)
    
            self.redirect("/modifyScene?scene=" + sceneId)

class Delete(webapp.RequestHandler):
    def post(self):
        import string
        from common.commonDB import deleteCorrespondence, deleteMesh, authorizedToDeleteCorrespondence, authorizedToDeleteMesh
        from google.appengine.api import users
        import calibrate
        
        pointList = self.request.get('points').split()
        meshList = self.request.get('meshes').split()
        sceneId = self.request.get('scene')
        scene = Scene.get_by_key_name('scene' + sceneId)
        if not scene:
            values = {'message': 'Scene ' + scene + ' does not exist.'}
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
            return
            
        user = users.get_current_user()
        
        if scene.is_private:
            if user != scene.owner and user.nickname() not in ADMIN_EMAILS:
                values = {
                    'message': 'You do not have permission to view this scene. Click <a href="http://www.projectlive3d.com/">here</a> to return to the main page.',
                    'username' : user.nickname(),
                    'logInURL' : logInURL,
                    'logOutURL' : logOutURL
                }
                self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
                return
        
        for p in pointList:
            corr = Correspondence.get_by_key_name('corr' + p);
            if authorizedToDeleteCorrespondence(corr, user):
                deleteCorrespondence(int(p))
            else:
                values = {'message': 'User ' + user.nickname() + ' is not allowed to delete Point ' + p + '.'}
                self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
                return
                
        for m in meshList:
            mesh = Mesh.get_by_key_name('mesh' + m);
            if authorizedToDeleteMesh(mesh, user):
                deleteMesh(int(m))
            else:
                values = {'message': 'User ' + user.nickname() + ' is not allowed to delete Mesh ' +mesh.name + '.'}
                self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
                return
        
        calibrate.calibrateCamera(scene)
            
        self.redirect("/modifyScene?scene=" + sceneId)

class DumpCorrespondences(webapp.RequestHandler):
    def get(self):
        from google.appengine.api import users
    
        user = users.get_current_user()
        if not user or user.nickname() not in ADMIN_EMAILS:
            loginURL = users.create_login_url("/")
            values = {
                'message' : loginURL
            }
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
            return
            
        # read input.
        sceneId = self.request.get('scene')
        
        # get the scene.
        scene = Scene.get_by_key_name('scene' + sceneId)
        
        # get all the correspondences.
        corrs = Correspondence.all().filter('scene =', scene).fetch(limit=1000)
        for c in corrs:
            self.response.out.write('%f %f %f %d %d' % (c.latitude, c.longitude, c.altitude, c.u, c.v))
            self.response.out.write('\n')
        
        # write the location of the camera.
        if scene.calibrated:
            self.response.out.write('%f %f %f NaN NaN' % (scene.cameraLatitude, scene.cameraLongitude, scene.cameraAltitude))


"""
View a page of scenes that user owns
"""
class MyScenes(webapp.RequestHandler):
    def get(self):
        from google.appengine.api import users
        user = users.get_current_user()
        
        logInURL = users.create_login_url(self.request.url)
        logOutURL = users.create_logout_url(self.request.url)
        if not user:
            username = None
        else:
            username = user.nickname()
            
        if username == None:
            values = {
                    'message': 'You are not logged in. Click <a href="http://www.projectlive3d.com/">here</a> to go to the main page.',
                    'username' : username,
                    'logInURL' :logInURL,
                    'logOutURL' :logOutURL
                    }
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
            return
        # default value
        searchFor = ""
        searchIn = -1

        # read the input.
        searchFor = self.request.get("searchstring")
        searchIn = self.request.get("searchin") #name=0, owner=1, url=2
        
        page = self.request.get("page")
        if not page:
            page = 1
        else:
            page = int(page)
        
        query = None
        if searchFor == "":
            query = Scene.all().filter("owner =",user).order("-calibrated").order("-ratingAverage").order("index")
            gc = query.count(10000)
            if not gc:
                values = {
                    'message': 'You didn\'t add any scenes yet. Use the contribute page to add one.',
                    'username' : username,
                    'logInURL' :logInURL,
                    'logOutURL' :logOutURL
                    }
                self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
                return
        else:
            if searchIn == "Name":
                query = Scene.all().filter("owner =",user).filter("name  =",searchFor).order("-calibrated").order("-ratingAverage").order("index")
            if searchIn == "URL":
                query = Scene.all().filter("owner =",user).filter("url  =",searchFor).order("-calibrated").order("-ratingAverage").order("index")
            gc = query.count(10000)
            if not gc:
                error = 'The search query did not generate any result. Couldn\'t find \''+searchFor+'\' in \''+searchIn+'\'. <A HREF="javascript:javascript:history.go(-1)">Go back to previous page</A> '
                
                values = {
                    'message': error,
                    'username' : username
                    }
                self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
                return
        
        scenes=query.fetch(RESULTS_PER_PAGE, (page-1)*RESULTS_PER_PAGE)
                
        lastPage = gc / RESULTS_PER_PAGE
        if (gc % RESULTS_PER_PAGE) > 0:
            lastPage = lastPage + 1
        
        pagesMarked = [page]
        firstGroup = []
        lastGroup = []
        prev = []
        next = []
        
        for scene in scenes:
            stars = [False] * 5
            for i in range(5):
                stars[i] = (scene.ratingAverage > i)
            scene.stars = stars
        
        for i in range(1,lastPage+1):
            if i < page and i <= FIRST_PAGES and i not in pagesMarked:
                firstGroup.append(i)
                pagesMarked.append(i)
            if i > page and lastPage - i < LAST_PAGES and i not in pagesMarked:
                lastGroup.append(i)
                pagesMarked.append(i)
            if i < page and page - i <= PREV_PAGES and i not in pagesMarked:
                prev.append(i)
                pagesMarked.append(i)
            if i > page and i - page <= NEXT_PAGES and i not in pagesMarked:
                next.append(i)
                pagesMarked.append(i)
        
        prevEllipsis = False
        nextEllipsis = False
        for i in range(1,lastPage+1):
            if i < page and i not in pagesMarked:
                prevEllipsis = True
            if i > page and i not in pagesMarked:
                nextEllipsis = True
            if prevEllipsis and nextEllipsis:
                break
            
        values = {
            'username' : username,
            'logInURL' :logInURL,
            'logOutURL' :logOutURL,
            'scenes' : scenes,
            'firstGroup'   : firstGroup,
            'prev'   : prev,
            'thisPage' : page,
            'next' : next,
            'lastGroup' : lastGroup,
            'prevEllipsis' : prevEllipsis,
            'nextEllipsis' : nextEllipsis
        }
        self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'myScenes.html', values))
            

"""
View a page of scenes.
"""
class ViewAll(webapp.RequestHandler):
    def get(self):
        from google.appengine.api import users
        user = users.get_current_user()
        
        logInURL = users.create_login_url(self.request.url)
        logOutURL = users.create_logout_url(self.request.url)
        if not user:
            username = None
        else:
            username = user.nickname()
            
        # default value
        searchFor = ""

        error = 0
        errormessage = ""

        # read the input.
        searchFor = self.request.get("searchstring")
        
        page = self.request.get("page")
        if not page:
            page = 1
        else:
            page = int(page)
        
        query = Scene.all().filter("is_private =",False).order("-calibrated").order("-ratingAverage").order("index")
        gc = query.count(10000)
        if not gc:
            error = 1
            errormessage = 'Apparently our growing library of scenes isn\'t so big.  Couldn\'t find any scenes.'
    
        if searchFor == "":
            issearch = 0
            searchmessage = ""
            scenes = query.fetch( RESULTS_PER_PAGE, (page-1)*RESULTS_PER_PAGE )
            
        else:
            issearch = 1
            searchmessage = 'Searching for \'' + searchFor + '\'.'

            # Google App Engine has terrible search string handling.  We only have a few hundred
            # scenes, so until we have the success disaster of having too many scenes that we
            # can't do a python search, just take all the search away from Google App Engine.
            scenes = query.fetch(1000)
            scenes = [s for s in scenes if (s.name and searchFor.lower() in s.name.lower()) or 
                                           (s.url and searchFor.lower() in s.url.lower())]
            
            gc = len(scenes)
            scenes = scenes[(page-1)*RESULTS_PER_PAGE:page*RESULTS_PER_PAGE]
            if gc == 0:
                error = 1
                errormessage = 'The search query did not generate any results. Try another search term.'
            
#            if searchIn == "Name":
#                #query = Scene.all().filter("is_private =",False).filter("name  =",searchFor).order("-calibrated").order("-ratingAverage").order("index")
#                query = Scene.all().filter("is_private =",False).filter("name >=", searchFor).filter("name <", searchFor+"\ufffd").order("-calibrated").order("-ratingAverage").order("index")
#            if searchIn == "Owner":
#                query = Scene.all().filter("is_private =",False).filter("owner =", searchFor).order("-calibrated").order("-ratingAverage").order("index")
#            if searchIn == "URL":
#                query = Scene.all().filter("is_private =",False).filter("url =", searchFor).order("-calibrated").order("-ratingAverage").order("index")
#            gc = query.count(10000)
#            if not gc:
#                error = 1
#                errormessage = 'The search query did not generate any results. Try another search term.'
                
                #values = {
                #    'message': error,
                #    'username' : username
                #    }
                #self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
                #return
        
        
        
        lastPage = gc / RESULTS_PER_PAGE
        if (gc % RESULTS_PER_PAGE) > 0:
            lastPage = lastPage + 1
        
        pagesMarked = [page]
        firstGroup = []
        lastGroup = []
        prev = []
        next = []
        
        for scene in scenes:
            stars = [False] * 5
            for i in range(5):
                stars[i] = (scene.ratingAverage > i)
            scene.stars = stars
        
        for i in range(1,lastPage+1):
            if i < page and i <= FIRST_PAGES and i not in pagesMarked:
                firstGroup.append(i)
                pagesMarked.append(i)
            if i > page and lastPage - i < LAST_PAGES and i not in pagesMarked:
                lastGroup.append(i)
                pagesMarked.append(i)
            if i < page and page - i <= PREV_PAGES and i not in pagesMarked:
                prev.append(i)
                pagesMarked.append(i)
            if i > page and i - page <= NEXT_PAGES and i not in pagesMarked:
                next.append(i)
                pagesMarked.append(i)
        
        prevEllipsis = False
        nextEllipsis = False
        for i in range(1,lastPage+1):
            if i < page and i not in pagesMarked:
                prevEllipsis = True
            if i > page and i not in pagesMarked:
                nextEllipsis = True
            if prevEllipsis and nextEllipsis:
                break

        values = {
            'username' : username,
            'logInURL' :logInURL,
            'logOutURL' :logOutURL,
            'scenes' : scenes,
            'issearch' : issearch,
            'searchmessage' : searchmessage,
            'error' : error,
            'errormessage' : errormessage,
            'firstGroup'   : firstGroup,
            'prev'   : prev,
            'thisPage' : page,
            'next' : next,
            'lastGroup' : lastGroup,
            'prevEllipsis' : prevEllipsis,
            'nextEllipsis' : nextEllipsis
        }
        self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'viewAll.html', values))

"""
Allows a user to rate on a scene.
"""
class Rate(webapp.RequestHandler):
    def get(self):
        from google.appengine.api import users
        import hashlib
        from common.commonDB import rateScene
        
        sceneId = self.request.get('scene')
        rating = self.request.get('rating')
        if not sceneId:
            values = {'message': 'No scene supplied.'}
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
            return
        
        isPTZ = False
        scene = Scene.get_by_key_name('scene' + str(sceneId))
        if not scene:
            scene = PTZScene.get_by_key_name('scene' + str(sceneId))
            if scene:
                isPTZ = True
            else:
                values = {'message': 'Scene ' + str(sceneId) + ' does not exist.'}
                self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
                return
            
        user = users.get_current_user()
        if not user:
            values = {'message': 'You must be logged in to vote.'}
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
            return
            
        try:
            rating = int(rating)
        except ValueError:
            values = {'message': 'Rating is non-numeric.'}
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
            return
            
        if rating > 5 or rating < 0:
            values = {'message': 'Rating is not in the alloted values.'}
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
            return
        
        keyname = hashlib.md5(user.nickname() + '|' + str(sceneId)).hexdigest()
        rateObj = Rating.get_by_key_name(keyname)
        if not rateObj:
            rateObj = Rating(key_name = keyname,
                             scene = scene,
                             user = user,
                             rating = rating)
            rateObj.put()
            rateScene(sceneId, rating)
            if isPTZ:
                self.redirect("/ptz/viewScene?scene=" + str(scene.index))
            else:
                self.redirect("/viewScene?scene=" + str(scene.index))
        else:
            values = {'message': 'This user has already rated this scene.'}
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))

"""
Writes a KMZ file with everything in the scene.
"""
class ToKMZ(webapp.RequestHandler):
    def get(self):
        import urllib2
        sceneId = self.request.get('scene')
        scene = Scene.get_by_key_name('scene' + sceneId)
        if not scene:
            values = {'message': 'Scene ' + scene + ' does not exist.'}
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
            return
        latitude  = self.request.get('latitude')
        longitude = self.request.get('longitude')
        altitude  = self.request.get('altitude')
        range     = self.request.get('range')
        heading   = self.request.get('heading')
        tilt      = self.request.get('tilt')
        
        if not latitude:
            latitude = scene.cameraLatitude
        if not longitude:
            longitude = scene.cameraLongitude
        if not altitude:
            altitude = scene.cameraAltitude
        if not heading:
            heading = scene.cameraHeading
        if not tilt:
            tilt = scene.cameraTilt
        
        meshes = Mesh.all().filter('scene =', scene)
        
        # TODO: write the kmz file.
        values = {
            'heading' : heading,
            'tilt' : tilt,
            'latitude' : latitude,
            'longitude' : longitude,
            'altitude' : altitude,
            'range' : range,
            'meshes' : meshes,
            'scene' : scene
        }
        docKML = template.render(TEMPLATE_DIRECTORY + 'doc.kml', values)
        files = [('doc.kml', docKML)]
        for mesh in meshes:
            meshStr = template.render(TEMPLATE_DIRECTORY + 'mesh.dae', {'mesh' : mesh})
            files.append( (str(mesh.index) + '.dae', meshStr))
        
        zipname = 'scene' + str(scene.index) + '.kmz'
        
        
        # write the zip file.
        import zipfile
        import StringIO
        zipstream=StringIO.StringIO()
        zfile = zipfile.ZipFile(zipstream, "w")
        
        for f in files:
            fname = f[0]
            fcontents = f[1]
            zfile.writestr(fname,fcontents)
        
        zfile.close()
        zipstream.seek(0)
        
        # create and return the output stream
        self.response.headers['Content-Type'] ='application/zip'
        self.response.headers['Content-Disposition'] = 'attachment; filename="' + zipname + '"' 
        while True:
            buf=zipstream.read(2048)
            if buf=="": 
                break
            self.response.out.write(buf)

"""
Acts as a proxy to the AMOS server.
"""
class AMOSDirListJpegs(webapp.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/xml'
        month = int(self.request.get('month'))
        year = int(self.request.get('year'))
        camera_id = int(self.request.get('camera_id'))
        url = AMOS_DIR_LIST_URL + '?camera_id=%d&month=%d&year=%d' % (camera_id, month, year)
        result = urlfetch.fetch(url, deadline=10)
        self.response.out.write(result.content)
  
"""
Displays the image when the user first added this scene.
"""
class RenderOriginalImage(webapp.RequestHandler):
    def get(self, sceneId):
        # convert the id to an int.
        try:
            sceneId = int(sceneId)
        except ValueError, e:
            self.response.out.write('Scene ' + sceneId + ' is non-numeric.')
            return;
        scene = Scene.get_by_key_name('scene' + str(sceneId))
        
        if (scene and scene.firstImage):
            self.response.headers['Content-Type'] = 'image/jpeg'
            self.response.out.write(scene.firstImage)
        else:
            self.response.out.write('Scene' + str(sceneId) + ' does not exist.')
    
"""
Displays the .DAE file required to display a mesh in Google Earth.
"""
class RenderMesh(webapp.RequestHandler):
    def get(self, meshId):
        # convert the id to an int.
        try:
            meshId = int(meshId)
        except ValueError, e:
            self.response.out.write('Mesh ' + meshId + ' is non-numeric.')
            return;
            
        # get the mesh.
        mesh = Mesh.get_by_key_name('mesh' + str(meshId))
        if mesh:
            # render the mesh.
            values = {
                'mesh' : mesh
            }
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'mesh.dae', values))
        else:
            self.response.out.write('Mesh ' + str(meshId) + ' does not exist.')

class MakePublic(webapp.RequestHandler):
    def post(self):
        sceneId = self.request.get('scene')
        scene = Scene.get_by_key_name('scene' + str(sceneId))
        scene.is_private = False
        scene.put()
        self.redirect("/viewScene?scene=" + str(scene.index))
        
"""
View a page of scenes that user owns
"""
class ScenesURL(webapp.RequestHandler):
    def get(self):
        from google.appengine.api import users
        user = users.get_current_user()
        if not user:
            username = ''
        else:
            username = user.nickname()
        
        logInURL = users.create_login_url(self.request.url)
        logOutURL = users.create_logout_url(self.request.url)
        # read the input.
        searchURL = self.request.get("url")
        print searchURL
        page = self.request.get("page")
        if not page:
            page = 1
        else:
            page = int(page)
        
        query1 = Scene.all().filter("owner =",user).filter("url  =",searchURL).order("-calibrated").order("-ratingAverage").order("index")
        query2 = Scene.all().filter("is_private =",False).filter("url  =",searchURL).order("-calibrated").order("-ratingAverage").order("index")
        
        gc1 = query1.count(10000)
        gc2 = query2.count(10000)
            
        scenes1=query1.fetch(RESULTS_PER_PAGE, (page-1)*RESULTS_PER_PAGE)
        scenes2=query2.fetch(RESULTS_PER_PAGE, (page-1)*RESULTS_PER_PAGE)
        
        gc = gc1 + gc2
        scenes = scenes1 + scenes2
        
        lastPage = gc / RESULTS_PER_PAGE
        if (gc % RESULTS_PER_PAGE) > 0:
            lastPage = lastPage + 1
        
        pagesMarked = [page]
        firstGroup = []
        lastGroup = []
        prev = []
        next = []
        
        for scene in scenes:
            stars = [False] * 5
            for i in range(5):
                stars[i] = (scene.ratingAverage > i)
            scene.stars = stars
        
        for i in range(1,lastPage+1):
            if i < page and i <= FIRST_PAGES and i not in pagesMarked:
                firstGroup.append(i)
                pagesMarked.append(i)
            if i > page and lastPage - i < LAST_PAGES and i not in pagesMarked:
                lastGroup.append(i)
                pagesMarked.append(i)
            if i < page and page - i <= PREV_PAGES and i not in pagesMarked:
                prev.append(i)
                pagesMarked.append(i)
            if i > page and i - page <= NEXT_PAGES and i not in pagesMarked:
                next.append(i)
                pagesMarked.append(i)
        
        prevEllipsis = False
        nextEllipsis = False
        for i in range(1,lastPage+1):
            if i < page and i not in pagesMarked:
                prevEllipsis = True
            if i > page and i not in pagesMarked:
                nextEllipsis = True
            if prevEllipsis and nextEllipsis:
                break
            
        values = {
            'username' : username,
            'logInURL' :logInURL,
            'logOutURL' :logOutURL,
            'scenes' : scenes,
            'firstGroup'   : firstGroup,
            'prev'   : prev,
            'thisPage' : page,
            'next' : next,
            'lastGroup' : lastGroup,
            'prevEllipsis' : prevEllipsis,
            'nextEllipsis' : nextEllipsis
        }
        self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'scenesURL.html', values))
"""Push DB changes onto the server"""

class ChangeDB(webapp.RequestHandler):
    def get(self):
        scenes = Scene.all().fetch(1000)
        
        gc = GlobalCounter.get_by_key_name('gc')
        if not gc:
            values = {'error': 'Apparently our growing library of scenes isn\'t so big.  Couldn\'t find any scenes.'}
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'home.html', values))
            return
        
        for scene in scenes:
            scene.is_private = False
            scene.put()
        self.redirect("/")
        
#delete scenes with less than 5 points
class DBCleanup(webapp.RequestHandler):
    def get(self):
        from common.commonDB import deleteCorrespondence, deleteMesh, deleteScene, deleteCalibration
        allScenes = Scene.all().order("-calibrated").order("-ratingAverage").order("index").fetch(200,100)
        for scene in allScenes:
            allCorrs = Correspondence.all().filter('scene = ', scene)
            numCorrs = allCorrs.count()
            if numCorrs <= 5:
                allMeshes = Mesh.all().filter('scene = ', scene)
                calib = Calibration.get_by_key_name('calib' + str(scene.index))
                for corr in allCorrs:
                    deleteCorrespondence(corr.index)
                for mesh in allMeshes:
                    deleteMesh(mesh.index)
                deleteCalibration(scene.index)
                deleteScene(scene.index)
        self.redirect("/")

class ImportData(webapp.RequestHandler):
    def get(self):
        from google.appengine.api import users
        import databaseTransfer
        
        user = users.get_current_user()
        if not user or user.nickname() not in ADMIN_EMAILS:
            loginURL = users.create_login_url("/")
            values = {
                'error' : loginURL
            }
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'home.html', values))
            return
        
        action = self.request.get('action')
        maxchanges = self.request.get('maxchanges')
        start = self.request.get('start')
        
        if not maxchanges:
            maxchanges = 1000000
        else:
            maxchanges = int(maxchanges)
        
        if not start:
            start = 0
        else:
            start = int(start)
        
        if action == 'scene':
            databaseTransfer.dbDump('scene', maxchanges)
        elif action == 'corr':
            databaseTransfer.dbDump('correspondence', maxchanges)
        elif action == 'mesh':
            databaseTransfer.dbDump('mesh', maxchanges)
        elif action == 'calibrate':
            databaseTransfer.calibrateAll(maxchanges=maxchanges, start=start)
        elif action == 'check':
            databaseTransfer.dbCheck()

class CameraInfo(webapp.RequestHandler):
    def get(self):
        sceneId = self.request.get('scene')
        url = self.request.get('url')
        
        scene = None
        if sceneId:
            scene = Scene.get_by_key_name('scene' + sceneId)
        elif url:
            scenes = Scene.all().filter('url =', url).fetch(1)
            if len(scenes) > 0:
                scene = scenes[0]
        if not scene:
            self.response.out.write(-1)
        else:
            outstr = '<scene>\n'
            outstr = outstr + '<index>' + str(scene.index) + '</index>'
            outstr = outstr + '<name>' + str(scene.name) + '</name>'
            outstr = outstr + '<url>' + str(scene.url) + '</url>'
            outstr = outstr + '<calibrated>' + str(scene.calibrated) + '</calibrated>'
            outstr = outstr + '<lat>' + str(scene.cameraLatitude) + '</lat>'
            outstr = outstr + '<lon>' + str(scene.cameraLongitude) + '</lon>'
            outstr = outstr + '<alt>' + str(scene.cameraAltitude) + '</alt>'
            outstr = outstr + '<heading>' + str(scene.cameraHeading) + '</heading>'
            outstr = outstr + '<tilt>' + str(scene.cameraTilt) + '</tilt>'
            outstr = outstr + '<roll>' + str(scene.cameraRoll) + '</roll>'
            if scene.calibrated:
                calib = Calibration.get_by_key_name('calib' + str(scene.index))
                outstr = outstr + '<focalX>' + str(calib.focalLengthX) + '</focalX>'
                outstr = outstr + '<focalY>' + str(calib.focalLengthY) + '</focalY>'
                outstr = outstr + '<skew>' + str(calib.skew) + '</skew>'
                outstr = outstr + '<opticalX>' + str(calib.imageCenterX) + '</opticalX>'
                outstr = outstr + '<opticalY>' + str(calib.imageCenterY) + '</opticalY>'
            
            outstr = outstr + '</scene>'
            self.response.headers['Content-Type'] = 'text/xml'
            self.response.out.write(outstr)

class DepthMap(webapp.RequestHandler):
    def get(self):
        # get the scene.
        sceneId = self.request.get('scene')
        scene = Scene.get_by_key_name('scene' + sceneId)
        
        # get the calibration.
        calibration = Calibration.get_by_key_name('calib' + str(sceneId));
        if not calibration:
            values = {'error': 'Scene ' + sceneId + ' is not calibrated.'}
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'home.html', values))
            return
            
        values = {'scene': scene, 'calibration': calibration}
        self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'depth.html', values))

class UserTest(webapp.RequestHandler):
    def get(self):
        import urllib2
        from google.appengine.api import urlfetch
        from google.appengine.api import users

        amosId = 5207
        user = users.get_current_user()

        if not user :
            logInURL = users.create_login_url(self.request.url)
            logOutURL = users.create_logout_url(self.request.url)
            
            values = {
                'username' : None,
                'logInURL' : logInURL,
                'logOutURL' : logOutURL
            }
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'contribute.html', values))
        else :
            username = user.nickname()
            logInURL = users.create_login_url(self.request.url)
            logOutURL = users.create_logout_url(self.request.url)

            # if there's an AMOS id, get the URL from there.
            try:
                result = urllib2.urlopen(AMOS_ID_TO_URL + str(amosId))
                result = result.read()
                if (result != '' and result[0:7] == 'http://'):
                    url = result
                else:
                    raise urlfetch.DownloadError
            except (urlfetch.DownloadError, urllib2.HTTPError):
                values = {
                    'message' : 'Couldn\'t load AMOS ID ' + amosId + '.',
                    'username' : username,
                    'logInURL' :logInURL,
                    'logOutURL' :logOutURL
                }
                self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'middle.html', values))
                return

            values = {
                'url' : url,
                'scene' : None,
                'createScn': True,
                'correspondences' : None,
                'meshes' : None,
                'amosId' : 5207,
                'calibration' : None,
                'username': username,
                'logInURL': logInURL,
                'logOutURL': logOutURL,
                'numPts': 0,
                'requiredNumCorrs' : CORRESPONDENCES_REQUIRED_FOR_CALIBRATION,
                'userTest' : True
            }
            self.response.out.write(template.render(TEMPLATE_DIRECTORY + 'build.html', values))

class PageNotFound(webapp.RequestHandler):
    def get(self):
        from google.appengine.api import users
        
        user = users.get_current_user()
        logInURL = users.create_login_url(self.request.url)
        logOutURL = users.create_logout_url(self.request.url)
        
        username = None
        if user:
            username = user.nickname()
        
        values = {
            'username' : username,
            'logInURL' : logInURL,
            'logOutURL' : logOutURL
        }
        self.response.out.write(template.render(TEMPLATE_DIRECTORY + '404.html', values))

def main():
    app = webapp.WSGIApplication([(r'/', Home),
                                  (r'/about.html', About),
                                  (r'/ptz/about.html', About),
                                  (r'/vfyw.html', VFYW),
                                  (r'/contribute.html', Contribute),
                                  (r'/contact.html', Contact),
                                  (r'/middle.html', Middle),
                                  (r'/sendMail/', SendMail),
                                  (r'/viewAll', ViewAll),
                                  (r'/build', build.Build),
                                  (r'/insert/', insert.Insert),
                                  (r'/viewScene', ViewScene),
                                  (r'/makePublic', MakePublic),
                                  (r'/scenesURL', ScenesURL),
                                  (r'/myScenes', MyScenes),
                                  (r'/embed', Embed),
                                  (r'/modifyScene', ModifyScene),
                                  (r'/delete', Delete),
                                  (r'/rate', Rate),
                                  (r'/adminModify', AdminModify),
                                  (r'/renderOriginalImage/(.+)\.jpg', RenderOriginalImage),
                                  (r'/renderMesh/(.+)\.dae', RenderMesh),
                                  (r'/amosDirListJpegs', AMOSDirListJpegs),
                                  (r'/toKMZ', ToKMZ),
                                  (r'/import', ImportData),
                                  (r'/publishStats', stats.PublishStatistics),
                                  (r'/stats', stats.Statistics),
                                  (r'/cameraInfo', CameraInfo),
                                  (r'/depthMap', DepthMap),
                                  (r'/dumpCorrespondences', DumpCorrespondences),
                                  (r'/errorDetection', errorDetection.ErrorDet),
                                  (r'/userTest', UserTest),
                                  (r'/.*', PageNotFound)
                                  ],
                                 debug=True)
    
    wsgiref.handlers.CGIHandler().run(app)
    
if __name__ == '__main__':
    main()
    